﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SynapseGaming.LightingSystem.Rendering;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using SynapseGaming.LightingSystem.Core;

namespace Brain.SunBurn
{
    /// <summary>
    /// SSAO Post Processor made by Filip. No optmizations were made(yet)
    /// </summary>
    public class SSAOPostProcessor : BaseRenderTargetPostProcessor
    {
        public Texture2D RandomTexture { get; protected set; }
        public Effect SsaoEffect { get; protected set; }
        public Effect SsaoFinal { get; protected set; }
        public Vector2 HalfPixel { get; protected set; }

        public bool OutputOnlySsaoMap { get; set; }

        private float jitter = 0.0001f;
        public float Jitter
        {
            get { return jitter; }
            set { jitter = value; }
        }

        private float intensity = 1.425f;
        public float Intensity
        {
            get { return intensity; }
            set { intensity = value; }
        }

        private float diffscale = 12.5f;
        public float Diffscale
        {
            get { return diffscale; }
            set { diffscale = value; }
        }

        private float whiteCorrection = 0.0f;
        public float WhiteCorrection
        {
            get { return whiteCorrection; }
            set { whiteCorrection = value; }
        }

        private float pixelRadius = 20.0f;
        public float PixelRadius
        {
            get { return pixelRadius; }
            set { pixelRadius = value; }
        }

        public SSAOPostProcessor()
            : base()
        {
            RandomTexture = Engine.Instance.ContentManager.Load<Texture2D>("Textures/SSAO_random");
            SsaoEffect = Engine.Instance.ContentManager.Load<Effect>("Effects/ssao");
            SsaoFinal = Engine.Instance.ContentManager.Load<Effect>("Effects/ssao_final");

            HalfPixel = new Vector2()
            {
                X = 0.5f / (float)Engine.Instance.GraphicsDevice.PresentationParameters.BackBufferWidth,
                Y = 0.5f / (float)Engine.Instance.GraphicsDevice.PresentationParameters.BackBufferHeight
            };
        }
        
        public override void ApplyPreferences(ISystemPreferences preferences)
        {
        }

        public override void EndFrameRendering()
        {
            GraphicsDevice device = Engine.Instance.GraphicsDevice;
            device.BlendState = BlendState.Opaque;
            device.DepthStencilState = DepthStencilState.None;
            device.RasterizerState = RasterizerState.CullNone;

            Viewport viewport = Engine.Instance.GraphicsDevice.Viewport;
            CustomFrameBufferCollection ssaoBuffers = this.SceneState.FrameBuffers.GetCustomFrameBufferCollection("ssao", true);

            if (ssaoBuffers.Count <= 0)
            {
                int width = base.ProcessorRenderTarget.Width;
                int height = base.ProcessorRenderTarget.Height;
                SurfaceFormat format2 = base.ProcessorRenderTarget.Format;
                ssaoBuffers.Add(null);
                //ssaoBuffers.Add(null);
                ssaoBuffers[0] = new RenderTarget2D(device, viewport.Width, viewport.Height, false, format2, DepthFormat.None, 0, RenderTargetUsage.PlatformContents);
                // 2nd render target if you want to blur the result
                //ssaoBuffers[1] = new RenderTarget2D(graphicsDevice, m_Viewport.Width, m_Viewport.Height, false, format2, DepthFormat.None, 0, RenderTargetUsage.PlatformContents);
                device.SetRenderTarget(null);
            }

            if (OutputOnlySsaoMap)
            {
                base.GraphicsDeviceManager.GraphicsDevice.SetRenderTarget(this.PreviousRenderTarget);
            }
            else
            {
                base.GraphicsDeviceManager.GraphicsDevice.SetRenderTarget(ssaoBuffers[0]);
            }

            RenderTarget2D depth = this.SceneState.FrameBuffers.GetBuffer(FrameBufferType.DeferredDepthAndSpecularPower, false);
            RenderTarget2D normal = this.SceneState.FrameBuffers.GetBuffer(FrameBufferType.DeferredNormalViewSpaceAndSpecular, false);

            SsaoEffect.CurrentTechnique = SsaoEffect.Techniques["SSAOTech1"];
            SsaoEffect.Parameters["DepthBuffer"].SetValue(depth);
            SsaoEffect.Parameters["NormalBuffer"].SetValue(normal);
            SsaoEffect.Parameters["RandomTexture"].SetValue(RandomTexture);
            SsaoEffect.Parameters["InvProj"].SetValue(Matrix.Transpose(SceneState.ProjectionToView));
            SsaoEffect.Parameters["Params"].SetValue(new Vector4(viewport.Width, viewport.Height, SceneState.Environment.VisibleDistance, intensity));
            SsaoEffect.Parameters["halfPixel"].SetValue(HalfPixel);
            SsaoEffect.Parameters["jitter"].SetValue(jitter);
            SsaoEffect.Parameters["diffScale"].SetValue(diffscale);
            SsaoEffect.Parameters["whiteCorrection"].SetValue(whiteCorrection);
            SsaoEffect.Parameters["pixelRadius"].SetValue(pixelRadius);

            SsaoEffect.CurrentTechnique.Passes[0].Apply();
            FullFrameQuad.Render(this.GraphicsDeviceManager.GraphicsDevice, viewport.Width, viewport.Height);

            if (!OutputOnlySsaoMap)
            {
                base.GraphicsDeviceManager.GraphicsDevice.SetRenderTarget(this.PreviousRenderTarget);

                SsaoFinal.CurrentTechnique = SsaoFinal.Techniques["Merge"];
                SsaoFinal.Parameters["SSAOTex"].SetValue(ssaoBuffers[0]);
                SsaoFinal.Parameters["SceneTexture"].SetValue(this.ProcessorRenderTarget);
                SsaoFinal.Parameters["halfPixel"].SetValue(HalfPixel);
                SsaoFinal.Parameters["weight"].SetValue(1);
                SsaoFinal.CurrentTechnique.Passes[0].Apply();
                FullFrameQuad.Render(this.GraphicsDeviceManager.GraphicsDevice, viewport.Width, viewport.Height);
            }
            device.BlendState = BlendState.Opaque;
            device.DepthStencilState = DepthStencilState.Default;
            device.RasterizerState = RasterizerState.CullCounterClockwise;

            base.EndFrameRendering();
        }
    }
}
